package org.wheel.plugins.starter.jdbc.pool.util;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import org.wheel.module.core.util.Page;
import org.wheel.plugins.starter.autoconfigure.DynamicDataSourceProperties;
import org.wheel.plugins.toolkit.jdbc.pool.util.LambdaSqlExecutionUtil;
import org.wheel.plugins.toolkit.jsqlparse.helper.ParamValueConvert;
import org.wheel.plugins.toolkit.sql.core.*;
import org.wheel.plugins.toolkit.sql.entity.BeanMeta;
import org.wheel.plugins.toolkit.sql.helper.FieldMeta;

import java.io.Serializable;
import java.util.Collection;
import java.util.Date;
import java.util.List;

public class BaseDaoUtil {

    // 直接使用 LogFactory 创建日志对象，Hutool 会自动检测项目中的日志框架（优先级：Slf4j > Log4j > Logback > JDK Logging）
    private static final Log log = LogFactory.get();

    /* 让 Spring 自动注入 */
    private static DynamicDataSourceProperties conn;

    public static void setConn(DynamicDataSourceProperties conn) {
        BaseDaoUtil.conn = conn;
    }

    /* ===================== 通用 CRUD ===================== */

    /* ---------- 根据主键查询 ---------- */
    public static <E> E selectById(Serializable id, Class<E> targetClass) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        LambdaQueryWrapper<E> wrapper = Wrappers.lambdaQuery(targetClass)
                .eq(primaryKeyMeta.getFieldName(), id);
        return LambdaSqlExecutionUtil.one(conn, wrapper, targetClass);
    }

    /* ---------- 批量主键查询 ---------- */
    public static <E> List<E> selectBatchIds(Collection<? extends Serializable> ids, Class<E> targetClass) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        LambdaQueryWrapper<E> wrapper = Wrappers.lambdaQuery(targetClass)
                .in(primaryKeyMeta.getFieldName(), ids);
        return LambdaSqlExecutionUtil.list(conn, wrapper, targetClass);
    }

    /* ---------- 分页查询 ---------- */
    public static <E> Page<E> selectPage(Page<E> page, LambdaQueryWrapper<E> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.page(conn, wrapper,
                page.getPageNo(), page.getPageSize(), targetClass);
    }

    /* ---------- 列表查询 ---------- */
    public static <E> List<E> selectList(LambdaQueryWrapper<E> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.list(conn, wrapper, targetClass);
    }

    /* ---------- 单条查询 ---------- */
    public static <E> E selectOne(LambdaQueryWrapper<E> wrapper, Class<E> targetClass) {
        return LambdaSqlExecutionUtil.one(conn, wrapper, targetClass);
    }

    /* ---------- 计数 ---------- */
    public static long selectCount(LambdaQueryWrapper<?> wrapper) {
        return LambdaSqlExecutionUtil.count(conn, wrapper);
    }

    /* ---------- 是否存在 ---------- */
    public static boolean exists(LambdaQueryWrapper<?> wrapper) {
        return selectCount(wrapper) > 0;
    }

    /* ---------- 插入 ---------- */
    public static <E> int insert(E entity, Class<E> targetClass, String opUserId) {
        LambdaInsertWrapper<E> iw = new LambdaInsertWrapper<>(targetClass).insert(entity);
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        applyInsertTime(iw, beanMeta, opUserId);
        return LambdaSqlExecutionUtil.save(conn, iw);
    }

    /* ---------- 插入并返回主键 ---------- */
    public static <E> E insertAndGetId(E entity, Class<E> targetClass, String opUserId) {
        LambdaInsertWrapper<E> iw = new LambdaInsertWrapper<>(targetClass).insert(entity);
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        applyInsertTime(iw, beanMeta, opUserId);
        return LambdaSqlExecutionUtil.saveAndGetId(conn, iw);
    }

    /* ---------- 批量插入 ---------- */
    public static <E> int[] insertBatch(Collection<E> entities, Class<E> targetClass, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        List<LambdaInsertWrapper<E>> iws = entities.stream()
                .map(entity -> new LambdaInsertWrapper<>(targetClass).insert(entity))
                .peek(b->applyInsertTime(b, beanMeta, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.saveBatch(conn, iws);
    }

    /* ---------- 根据主键更新 ---------- */
    public static <E> int updateById(E entity, Class<E> targetClass, boolean ignoreNull, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        LambdaUpdateWrapper<E> uw = Wrappers.lambdaUpdate(targetClass)
                .eq(primaryKeyMeta.getFieldName(), getIdValue(entity, primaryKeyMeta))
                .update(entity, ignoreNull);
        applyUpdateTime(uw, beanMeta, opUserId);
        return LambdaSqlExecutionUtil.update(conn, uw);
    }

    /* ---------- 批量更新 ---------- */
    public static <E> int[] updateBatch(Collection<E> entities, Class<E> targetClass, boolean ignoreNull, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        List<LambdaUpdateWrapper<E>> uws = entities.stream()
                .map(entity -> Wrappers.lambdaUpdate(targetClass)
                        .eq(primaryKeyMeta.getFieldName(), getIdValue(entity, primaryKeyMeta))
                        .update(entity, ignoreNull))
                .peek(b->applyUpdateTime(b, beanMeta, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }

    /* ---------- 根据主键软删除 ---------- */
    public static <E> int logicRemoveById(Serializable id, Class<E> targetClass, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        LambdaUpdateWrapper<E> uw = Wrappers.lambdaUpdate(targetClass)
                .eq(primaryKeyMeta.getFieldName(), id);
        applyLogicDelete(uw, beanMeta, opUserId);
        return LambdaSqlExecutionUtil.update(conn, uw);
    }
    /* ---------- 条件软删除 ---------- */
    public static <E> int logicRemove(LambdaUpdateWrapper<E> wrapper, Class<E> targetClass, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        applyLogicDelete(wrapper, beanMeta, opUserId);
        return LambdaSqlExecutionUtil.update(conn, wrapper);
    }

    /* ---------- 批量删除 ---------- */
    public static <E> int[] deleteBatchIds(Collection<? extends Serializable> ids, Class<E> targetClass) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        List<LambdaDeleteWrapper<E>> delWrappers = ids.stream()
                .map(id->Wrappers.lambdaDelete(targetClass)
                        .eq(primaryKeyMeta.getFieldName(), id)).toList();
        return LambdaSqlExecutionUtil.deleteBatch(conn, delWrappers);
    }

    /* ---------- 批量软删除 ---------- */
    public static <E> int[] logicDeleteBatchIds(Collection<? extends Serializable> ids, Class<E> targetClass, String opUserId) {
        BeanMeta beanMeta = BeanMeta.of(targetClass);
        FieldMeta primaryKeyMeta = beanMeta.getFields()
                .stream()
                .filter(FieldMeta::isPrimaryKey)
                .findFirst()
                .orElse(null);
        assertPrimaryKey(primaryKeyMeta, targetClass);
        List<LambdaUpdateWrapper<E>> uws = ids.stream()
                .map(id->Wrappers.lambdaUpdate(targetClass)
                        .eq(primaryKeyMeta.getFieldName(), id))
                .peek(b->applyLogicDelete(b, beanMeta, opUserId))
                .toList();
        return LambdaSqlExecutionUtil.updateBatch(conn, uws);
    }

    private static void assertPrimaryKey(FieldMeta primaryKeyMeta, Class<?> targetClass) {
        if (primaryKeyMeta == null) {
            throw new IllegalStateException(
                    "实体 " + targetClass.getName() + " 未标记主键，无法执行基于主键的操作");
        }
    }

    private static Serializable getIdValue(Object entity, FieldMeta primaryKeyMeta) {
        // 1. 字段名
        String pkFieldName = primaryKeyMeta.getFieldName();   // 例如 "id"
        // 2. 用 Hutool 直接取值
        Object value = ReflectUtil.getFieldValue(entity, pkFieldName);
        if (value == null) {
            throw new IllegalArgumentException(
                    "实体 " + entity.getClass().getSimpleName() + " 的主键字段 " + pkFieldName + " 值为 null");
        }
        // 3. 强转
        if (!(value instanceof Serializable)) {
            throw new IllegalArgumentException(
                    "主键字段 " + pkFieldName + " 必须实现 java.io.Serializable");
        }
        return (Serializable) value;
    }

    private static void applyLogicDelete(LambdaUpdateWrapper<?> wrapper, BeanMeta beanMeta, String opUserId) {
        if (beanMeta.hasLogicDelete()) {
            wrapper.eq(beanMeta.getLogicDeleteField().getColumn(), 1);
        }
        applyUpdateTime(wrapper, beanMeta, opUserId);
    }

    private static void applyUpdateTime(LambdaUpdateWrapper<?> wrapper, BeanMeta beanMeta, String opUserId) {
        if (beanMeta.hasUpdateBy() && StrUtil.isNotBlank(opUserId)) {
            wrapper.set(beanMeta.getUpdateByField().getColumn(), opUserId);
        }
        if (beanMeta.hasUpdateTime()) {
            wrapper.set(beanMeta.getUpdateTimeField().getColumn(), new Date());
        }
    }

    private static void applyInsertTime(LambdaInsertWrapper<?> wrapper, BeanMeta beanMeta, String opUserId) {
        if (beanMeta.hasCreateBy() && StrUtil.isNotBlank(opUserId)) {
            wrapper.set(beanMeta.getCreateByField().getColumn(), opUserId);
        }
        if (beanMeta.hasCreateTime()) {
            wrapper.set(beanMeta.getCreateTimeField().getColumn(), new Date());
        }
        if (beanMeta.hasUpdateBy() && StrUtil.isNotBlank(opUserId)) {
            wrapper.set(beanMeta.getUpdateByField().getColumn(), opUserId);
        }
        if (beanMeta.hasUpdateTime()) {
            wrapper.set(beanMeta.getUpdateTimeField().getColumn(), ParamValueConvert.convertValue(beanMeta.getUpdateTimeField().getFieldType(), System.currentTimeMillis() + ""));
        }
        if (beanMeta.hasLogicDelete()) {
            wrapper.set(beanMeta.getLogicDeleteField().getColumn(), ParamValueConvert.convertValue(beanMeta.getLogicDeleteField().getFieldType(), "0"));
        }
    }
}